﻿
namespace TingSLsocket

open System
open System.IO
open TVA
open TVA.Parsing

type Header() =
    let mutable _size = 0
    let mutable _command = 0

    static member BinLength = 8

    member this.Size 
        with get() = _size
        and set(v) = 
            if v < 0 then
                failwith "size must >= 0"
            _size <- v

    member this.Command 
        with get() = _command
        and set(v) = 
            if v < 0 then
                failwith "command must >= 0"
            _command <- v

    member this.Serialize (buffer: byte[],start : int) =
        let mutable offset =  start

        EndianOrder.LittleEndian.CopyBytes(_size,buffer,offset)
        offset <- offset + 4

        EndianOrder.LittleEndian.CopyBytes(_command,buffer,offset)
        offset <- offset + 4

        offset - start

    member this.Deserialize (buffer : byte[],start : int) =
        let mutable offset = start

        this.Size <- EndianOrder.LittleEndian.ToInt32(buffer,offset)
        offset <- offset + 4

        this.Command <- EndianOrder.LittleEndian.ToInt32(buffer,offset)
        offset <- offset + 4

        offset - start

type ParserState =
    | Head = 0
    | Body = 1

[<Sealed>]
type CommandParser() =
    inherit BinaryImageParserBase()

    // -------------------------- primary constructor
    let m_evtCmdParsed = new Event<int*byte[]*int*int>()
    let m_header = new Header()
    let mutable m_state = ParserState.Head

    let parseHead buffer offset size =
        if size >= Header.BinLength then
            let parsed = m_header.Deserialize(buffer,offset)

            if m_header.Size > 0 then
                m_state <- ParserState.Body
            elif m_header.Size = 0 then
                // only has command, no parameter
                m_evtCmdParsed.Trigger(m_header.Command,null,0,0)
            else
                failwith "size cannot be negative"

            parsed
        else
            0

    let parseBody buffer offset size =
        if size >= m_header.Size then
            m_evtCmdParsed.Trigger(m_header.Command,buffer,offset,m_header.Size)

            m_state <- ParserState.Head
            m_header.Size
        else
            0

    // -------------------------- 
    [<CLIEvent>]
    member this.CmdParsedEvt = m_evtCmdParsed.Publish

    // -------------------------- override methods
    override this.Start() =
        base.Start()
        // to deal with re-connect
        // clear the remaining unparsed buffer left by previous connection
        this.UnparsedBuffer <- null

    override this.ProtocolUsesSyncBytes
        with get() = false

    override this.ParseFrame(buffer,offset,size) =
        match m_state with
        | ParserState.Head -> 
            parseHead buffer offset size
        | ParserState.Body ->
            parseBody buffer offset size
        | _ ->
            failwith "impossible parser state"

